/* This is the framework code for producer/consumer.
 * Use "gcc PC.c -o PC -lpthread" to compile.
 * TODO: You need to complete two functions:
 *       producer and consumer
 */

#include <pthread.h>
#include <stdio.h>

#define MAX 	10		/* Maxlen for buffer */
#define LOOPS 	100000 	/* Total production and consumption */

/* The **buffer** is a circular queue */
int buffer[MAX];
int total = 0;
int head = 0;
int tail = 0;

/* TODO: You may need to declare some variables, mutex or condition variables */
pthread_cond_t production_ready = PTHREAD_COND_INITIALIZER;
pthread_cond_t consumer_ready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;

/* We complete put and get.
 * And you can only use put and get to operate buffer.  
 */

/* Put value at the end of buffer */
void put(int value)
{
	buffer[tail] = value;
	tail = (tail + 1) % MAX;
	total++;
	printf("put: %d\n", value);
}

/* Get the value at the beginning of buffer */
int get()
{
	int tmp = buffer[head];
	head = (head + 1) % MAX;
	total--;
	printf("get: %d\n", tmp);
	return tmp;
}

void *producer(void *arg)
{
	int i;
	for (i = 0; i < LOOPS; i++)
	{
		/* TODO: Complete your code in the loop body, 
		 *       whether it is above or below "put(i);".
		 */

		pthread_mutex_lock(&buffer_mutex);
		while (MAX == total)
			pthread_cond_wait(&consumer_ready, &buffer_mutex);
		put(i);
		pthread_mutex_unlock(&buffer_mutex);
		pthread_cond_signal(&production_ready);

	}
}

void *consumer(void *arg)
{
	int i;
	for (i = 0; i < LOOPS; i++)
	{
		/* TODO: Complete your code in the loop body,
		 *       whether it is above or below "get(i);".
		 */

		pthread_mutex_lock(&buffer_mutex);
		while (0 == total)
			pthread_cond_wait(&production_ready, &buffer_mutex);
		int tmp = get();
		pthread_mutex_unlock(&buffer_mutex);
		pthread_cond_signal(&consumer_ready);
	}
}

int main(int argc, char *argv[])
{
	pthread_t p, c;
	int rc;
	char argP[] = "Producer";
	char argC[] = "Consumer";

	/* You can initialize here. */

	printf("Start!\n");

	/* Create two threads.
	 * Actually, we don't use the parameters of producer and consumer.
	 */
	rc = pthread_create(&p, NULL, producer, (void *)&argP);
	rc = pthread_create(&c, NULL, consumer, (void *)&argC);

	/* Wait */
	rc = pthread_join(p, NULL);
	rc = pthread_join(c, NULL);
	printf("Finish!\n");

	return 0;
}
